package org.chen.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.chen.config.JsoupConnectionPool;
import org.chen.mapper.CommentRuleMapper;
import org.chen.model.CommentData;
import org.chen.model.CommentRule;
import org.jsoup.Connection;
import org.jsoup.HttpStatusException;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.retry.Retry;

import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeoutException;

@Service
@Slf4j
public class CommentService {

    @Autowired
    private CommentRuleMapper commentRuleMapper;

    @Autowired
    private SmartCommentService smartCommentService;

    @Autowired
    private WebClient webClient;

    @Autowired
    private ObjectMapper objectMapper;  // JSON处理

    @Autowired
    private JsoupConnectionPool jsoupConnectionPool;

    /**
     * 发送评论 - 优化版本
     */
    public Mono<String> postComment(String targetUrl, CommentData commentData) {
        return Mono.defer(() -> {
            log.info("postComment:{}", targetUrl);
            try {
                String domain = extractDomain(targetUrl);

                // 查找匹配规则
                CommentRule rule = commentRuleMapper.findByUrlPattern(domain);

                if (rule != null) {
                    return sendCommentWithRule(targetUrl, commentData, rule);
                } else {
                    return postCommentAndSaveRule(targetUrl, commentData);
                }

            } catch (Exception e) {
                log.error("Failed to post comment: ", e);
                return Mono.error(e);
            }
        });
    }

    private String extractDomain(String url) {
        try {
            URL urlObj = new URL(url);
            return urlObj.getHost();
        } catch (MalformedURLException e) {
            log.error("Invalid URL: {}", url);
            return url;
        }
    }

    /**
     * 使用规则发送评论 - 异步优化版本
     */
    private Mono<String> sendCommentWithRule(String targetUrl, CommentData commentData, CommentRule rule) {
        return getPageContentAsync(targetUrl)
                .flatMap(document -> {
                    Element form = findCommentForm(document);
                    if (form == null) {
                        return Mono.error(new RuntimeException("Comment form not found"));
                    }

                    // 构建表单数据
                    MultiValueMap<String, String> formData =
                            smartCommentService.buildFormDataFromRule(rule, commentData, form);

                    String actionUrl = rule.getActionUrl().replace("{url}", targetUrl);

                    return sendCommentAsync(actionUrl, formData)
                            .doOnSuccess(result -> updateRuleSuccess(rule))
                            .doOnError(error -> updateRuleFailure(rule));
                });
    }

    /**
     * 异步获取页面内容
     */
    private Mono<Document> getPageContentAsync(String url) {
        return Mono.fromCallable(() -> {
            try {
                Connection conn = jsoupConnectionPool.getConnection(url)
                        .referrer("https://www.google.com")
                        .cookies(buildRandomCookies())
                        .maxBodySize(0);

                return conn.get();
            } catch (HttpStatusException e) {
//            log.error("Failed to get page content with Jsoup - HTTP error: ", e);
                throw e;  // 直接抛出，让上层处理
            } catch (SSLHandshakeException e) {
//            log.error("Failed to get page content with Jsoup - SSL error: ", e);
                throw e;  // 直接抛出，让上层处理
            } catch (IOException e) {
//            log.error("Failed to get page content with Jsoup: ", e);
                throw new RuntimeException("Failed to fetch page content: " + e.getMessage(), e);
            }
        }).subscribeOn(Schedulers.boundedElastic());
    }

    /**
     * 发送评论并保存规则 - 异步优化版本
     */
    private Mono<String> postCommentAndSaveRule(String targetUrl, CommentData commentData) {
        return getPageContentAsync(targetUrl)
                .flatMap(document -> {
                    Element form = findCommentForm(document);
                    if (form == null) {
                        return Mono.error(new RuntimeException("Comment form not found"));
                    }

                    try {
                        // 分析并保存规则
                        CommentRule newRule = analyzeAndSaveRule(targetUrl, form);

                        // 构建表单数据
                        MultiValueMap<String, String> formData =
                                smartCommentService.buildSmartFormData(form, commentData);

                        String actionUrl = form.hasAttr("action") ?
                                form.attr("abs:action") : targetUrl;

                        return sendCommentAsync(actionUrl, formData)
                                .doOnSuccess(result -> updateRuleSuccess(newRule))
                                .doOnError(error -> updateRuleFailure(newRule));
                    } catch (Exception e) {
                        return Mono.error(e);
                    }
                });
    }

    /**
     * 异步发送评论请求 - 优化版本
     */
    private Mono<String> sendCommentAsync(String actionUrl, MultiValueMap<String, String> formData) {
        String cookie = generateRandomCookie();

        return webClient.post()
                .uri(actionUrl)
                .headers(headers -> {
                    headers.add("Cookie", cookie);
                })
                .bodyValue(formData)
                .exchangeToMono(response -> {
                    // 记录状态码
                    HttpStatus status = response.statusCode();
                    log.debug("Response status for {}: {}", actionUrl, status);

                    if (status.is3xxRedirection() || status.is2xxSuccessful()) {
                        // 处理重定向
                        String location = response.headers().asHttpHeaders().getFirst(HttpHeaders.LOCATION);
                        // 确保在读取完头部信息后释放响应
                        return response.releaseBody()
                                .then(Mono.just(Objects.requireNonNullElse(location, actionUrl)));
                    }

                    // 处理响应体
                    return response.bodyToMono(String.class)
                            .defaultIfEmpty("")
                            .flatMap(body -> {
                                // 记录错误响应
                                log.debug("Error response for {}:  - {}", actionUrl, status);
                                return Mono.error(new RuntimeException(
                                        String.format("Comment failed with status %s:", status )));
                            });
                })
                .doOnError(error -> log.error("Error sending comment to {}: {}",
                        actionUrl, error.getMessage()))
                .timeout(Duration.ofSeconds(30))
                .retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
                        .filter(this::shouldRetry))
                .doFinally(
                        signalType -> {
                            log.debug("Request completed with signal: {} for URL: {}",
                                    signalType, actionUrl);
                        }
                );
    }

    /**
     * 判断是否应该重试的改进版本
     */
    private boolean shouldRetry(Throwable throwable) {
        if (throwable instanceof TimeoutException ||
                throwable instanceof IOException) {
            return true;
        }

        String message = throwable.getMessage();
        if (message == null) {
            return false;
        }

        // 检查常见的可重试错误
        return message.contains("Connection reset") ||
                message.contains("Connection refused") ||
                message.contains("timeout") ||
                message.contains("Too Many Requests") ||
                message.contains("Service Unavailable");
    }

    private CommentRule analyzeAndSaveRule(String targetUrl, Element form) throws JsonProcessingException {
        Map<String, String> fields = smartCommentService.analyzeFormFields(form);
        Set<String> hiddenFieldNames = smartCommentService.analyzeHiddenFieldNames(form);

        CommentRule rule = new CommentRule();
        rule.setUrlPattern(extractDomain(targetUrl));
        rule.setFormSelector(smartCommentService.generateFormSelector(form));
        rule.setActionUrl(form.hasAttr("action") ? form.attr("abs:action") : "{url}");

        // 设置字段映射
        rule.setCommentField(fields.get("comment"));
        rule.setAuthorField(fields.get("author"));
        rule.setEmailField(fields.get("email"));
        rule.setUrlField(fields.get("url"));

        // 只保存隐藏字段的名称
        rule.setHiddenFieldNames(objectMapper.writeValueAsString(hiddenFieldNames));

        rule.setUseCount(1);
        rule.setSuccessRate(100.0);

        commentRuleMapper.insert(rule);
        return rule;
    }

    /**
     * 更新发送成功统计
     */
    private void updateRuleSuccess(CommentRule rule) {
        try {
            double newRate = (rule.getSuccessRate() * rule.getUseCount() + 100) / (rule.getUseCount() + 1);
            commentRuleMapper.updateRuleStats(rule.getId(), newRate);
        } catch (Exception e) {
            log.error("Failed to update rule success stats: {}", e.getMessage());
        }
    }

    /**
     * 更新发送失败统计
     */
    private void updateRuleFailure(CommentRule rule) {
        try {
            double newRate = (rule.getSuccessRate() * rule.getUseCount()) / (rule.getUseCount() + 1);
            commentRuleMapper.updateRuleStats(rule.getId(), newRate);
        } catch (Exception e) {
            log.error("Failed to update rule failure stats: {}", e.getMessage());
        }
    }

    /**
     * 查找评论表单
     */
    private Element findCommentForm(Document document) {
        // 按优先级查找评论表单
        List<String> selectors = Arrays.asList(
                "form[id~=(?i).*comment.*]",      // 匹配 id 包含 "comment" 的表单
                "form[class~=(?i).*comment.*]",  // 匹配 class 包含 "comment" 的表单
                "form[action~=(?i).*comment.*]"  // 匹配 action 包含 "comment" 的表单
        );

        for (String selector : selectors) {
            Element form = document.select(selector).first();
            if (form != null) {
                log.info("Found comment form using selector: {}", selector);
                return form;
            }
        }

        log.warn("No comment form found using common selectors.");
        return null;
    }

    private Document getPageContentWithJsoup(String url) throws HttpStatusException, SSLHandshakeException {
        try {
            // 添加引用来源
            // 不限制响应大小
            // 添加随机cookie
            // 使用连接池获取连接
            return jsoupConnectionPool.getConnection(url)
                    .referrer("https://www.google.com")
                    .cookies(buildRandomCookies())
                    .get();
        } catch (HttpStatusException e) {
//            log.error("Failed to get page content with Jsoup - HTTP error: ", e);
            throw e;  // 直接抛出，让上层处理
        } catch (SSLHandshakeException e) {
//            log.error("Failed to get page content with Jsoup - SSL error: ", e);
            throw e;  // 直接抛出，让上层处理
        } catch (IOException e) {
//            log.error("Failed to get page content with Jsoup: ", e);
            throw new RuntimeException("Failed to fetch page content: " + e.getMessage(), e);
        }
    }

    private Map<String, String> buildRandomCookies() {
        Map<String, String> cookies = new HashMap<>();
        cookies.put("_ga", "GA" + System.currentTimeMillis());
        cookies.put("_gid", "GID" + new Random().nextInt(1000000));
        return cookies;
    }

    private String generateRandomCookie() {
        long currentTime = System.currentTimeMillis();
        int randomValue = new Random().nextInt(1000000);
        return "_ga=GA" + currentTime + "; _gid=GID" + randomValue;
    }
}